/*
   EXAMPLE

   new Dropdown($('widget-dropdown-x'),
                "thepage.php",
                {'zerovalue':0,      // when no selection or new value use this as value
                 'listfull':false,   // show  hint's full list in any case (with limit)
                 'addable':true,     // accept new values
                 'limit':10});       // hint's list limit

   // [FILTERING]       the widget will call thepage.php?data=xxx&limit=10   [RETURN] a json array with 0 or more elements [(1,Alfa),(2,Beta),(3,Gamma)]
   // [FIRST SELECTION] the widget will call thepage.php?id=3                [RETURN] a json array with 1 or 0 elements [(3,Gamma)]
   //the widget result is the same in this 2 examples
   <input type="text" id="widget-dropdown-x" value="3" />          <!-- one XHR call on load.[FIRST SELECTION] to fill -->
   <input type="text" id="widget-dropdown-x" value="[3,'Gamma']" /> <!-- no XHR calls on load -->


*/


Form.Validator.add('validate-dropdown', {
    errorMsg: 'Seleziona un valore valido',
    test: function(el,props){
        if (el.instance.inputValue.get('value') == props['validate-dropdown']) return false;
        else return true;
    }
});

var Dropdown = new Class({
    Implements: [Options],//,Events],
    zerovalue: 0,
    color: '',//'#fbfcc0', //eventually support for "new" input
    debug: false,

    label_postfix: '_label',
    dropdown_postfix: '_dropdown',
    value_postfix: '_value',

    options: { 'addable' : null,  //addable
               'listfull' : null, //always full list
               'limit': null },   //max list length

    url: '',
    addable: false,
    listfull: false,
 
    target: null,
    dropdown: null,
    inputLabel: null,
    inputValue: null,
    form: null,

    initialize: function(target, url, options) {
        this.target = target;
        this.setOptions(options);

        this.url       = url                    || this.target.get('dropdown:url')      || '';
        this.addable   = this.options.addable   || this.target.get('dropdown:addable')  || false;
        this.listfull  = this.options.listfull  || this.target.get('dropdown:listfull') || false;
        this.limit     = this.options.limit     || this.target.get('dropdown:limit')    || 0;
        this.zerovalue = this.options.zerovalue || 0;

        el_id = this.target.get('id');
        el_name = this.target.get('name');
        label_name = el_name + this.label_postfix;
        var dropdown_id = null, label_id = null;
        if (el_id) {
            dropdown_id = el_id + this.dropdown_postfix;
            label_id = el_id + this.label_postfix;
        }

        var start;
        try      { start = JSON.decode(this.target.value); }
        catch(e) { start = this.target.value; }
        var fulldata = false;
        if (typeOf(start)=='array') {
            fulldata = true;
            start_value = start[0];
            start_label = start[1];
        } else {
            start_value = start;
            start_label = '';
        }

        this.inputLabel = this.target;
        this.inputLabel.instance = this;

        // edit the inputLabel (change id to inputLabel.id+_label)
        this.inputLabel.set('name',label_name);
        if (label_id) this.inputLabel.set('id',label_id);
        this.inputLabel.setAttribute('value',start_label); //use setAttribute to edit the value in DOM (needed to reset)

        // create input for value (get id from inputLabel)
        this.inputValue = Element('input',{'name':el_name,'type':(this.debug?'text':'hidden'), 'value':start_value});
        if (el_id) this.inputValue.set('id',el_id);
        this.inputValue.inject(this.target,'after');
        this.inputValue.setAttribute('value',start_value); //use setAttribute to edit the value in DOM (needed to reset)
        if (!this.addable) this.inputLabel.addClass('validate-dropdown:\''+this.zerovalue+'\'');

        // create the dropdown
        this.dropdown = Element('div',{'class':'dropdown','html':'<ul></ul>'});
        if (dropdown_id) this.dropdown.set('id',dropdown_id);

        this.dropdown.inject(this.target,'after');
        this.dropdown.hide();

        // create the button
        div = Element('div',{'style':'position:relative'});
        div.inject(this.inputLabel,'after');
        this.inputLabel.inject(div);

        if (this.isactive()) {
            this.button = Element('div',{'style':'border:6px solid transparent; border-top:7px solid #666; position:absolute; top:10px; cursor:pointer;'});
            this.button.setStyles({'left':this.inputLabel.getSize()['x']-20});
            this.button.inject(this.inputLabel,'before');
        }

        /****/

        if (!fulldata)
            this.get(); //filled only with VALUE and not LABEL

        this.form = this.inputLabel.getParent('form');
        this.form.addEvent('reset', function(e) { this.inputLabel.setStyle('background-color',''); }.bind(this));

        if (this.button)
            this.button.addEvent('click',function(e) {
                if (!this.isactive()) return;
                this.inputLabel.setFocus();
                this.request(e,this.listfull);
            }.bind(this));

        this.inputLabel.addEvent('dblclick',function(e) { 
            if (!this.isactive()) return;
            this.request(e,this.listfull);
        }.bind(this));

        this.inputLabel.addEvent('keyup',function(e) {
            if (!this.isactive()) return;
            if (e.code!=9 && e.code!=27) this.request(e);
        }.bind(this));

        this.inputLabel.addEvent('keydown',function(e) {
            if (e.code==9) {
                this.close();
                dropdown_anchor = this.dropdown.getElement('a');
                if (dropdown_anchor) this.setpair(dropdown_anchor.get('drop:label'), dropdown_anchor.get('drop:value'));
            } else if (e.code==27) {
                this.close();
            }
        }.bind(this));

    },

    isactive: function() {
        return !(this.inputLabel.get('readonly') || this.inputLabel.get('disabled'))
    },

    get: function() {
        var dataRequest = new Request.JSON({url: this.url, onSuccess: function(els){
             if (els.length==1) {
                 this.inputLabel.setAttribute('value',els[0][1]); //use setAttribute to edit the value in DOM (needed to reset)
                 this.inputLabel.set('value',els[0][1]); //use setAttribute to edit the visible value
             }

        }.bind(this)
        }).get({'id':this.inputValue.value});
    },

    request: function(event,listfull) {
        var drop = this.dropdown;
        var dropvalue = this.inputValue;
        var dropinput = this.inputLabel;
        dropinput.setStyle('background-color',this.color);
        dropvalue.value = this.zerovalue;

        var equal = this.zerovalue;
        var jsonRequest = new Request.JSON({url: this.url, onSuccess: function(els){
            this.dropdown.getChildren('ul').destroy(); 
            if (!this.dropdown.isDisplayed()) {
                el_size = this.target.getSize();
                el_pos = this.target.getPosition();
                dropdown_y = el_size['y']+el_pos['y']-2;
                dropdown_x = el_pos['x'];
                dropdown_width = el_size['x']-2;
                this.dropdown.setStyles({'top':dropdown_y,'left':dropdown_x,'width':dropdown_width});
                this.dropdown.show();
                $(document.body).addEvent('click', function(e) { this.close(); }.bind(this));
            }
            ul = Element('ul');
            // els is like ((1,A),(2,B),(3,C))
            if ((this.inputLabel.get('value') != '') && (this.addable == 1)) {
                els = els.concat(([[this.zerovalue,this.inputLabel.get('value')]])); //NEW LINE
                //els = ([[this.zerovalue,this.inputLabel.get('value')]]).concat(els); //NEW LINE (new at first, generate error)
            }
            els.each(function(el) {
                el_value = el[0];
                el_label = el[1];
                isnew = el_value==this.zerovalue;
                if (isnew && equal!=this.zerovalue) return; //match! no print new line
                el_droplabel = el_label.split('\n')[0].replace(/<[^>]+>/gi,'');     //support multiline label (first line is the LABEL)
                el_label = el_label.replace(/\n/gi,'<br/>');  // more lines appears only in dropdown and not in the input box
                var li;
                if (isnew) {
                    li = Element('li',{'class':'new'});
                    el_html = el_label + ' <em>&lt;new&gt;</em>';
                } else {
                    li = Element('li');
                    el_html = el_label;
                    tofind = this.inputLabel.value.replace(new RegExp("([{}\(\)\^$&.\*\?\/\+\|\[\\\\]|\]|\-)","g"), "\\$1");
                    if (tofind) el_html = el_label.replace(new RegExp("("+tofind+")","i"),'<span class="highlighted">$1</span>');
                    if (this.debug) el_html += ' <em>&lt;'+el_value+'&gt;</em>';
                }
                var a = Element('a',{'html':el_html,'drop:label':el_droplabel,'drop:value':el_value,'tabindex':-1,'href':'#'});
                var that = this;
                a.addEvent('click', function(e) { 
                    that.close();
                    that.setpair(this.get('drop:label'),this.get('drop:value'));
                    return false;
                });
                a.inject(li);
                li.inject(ul);
                if (!isnew && this.inputLabel.value==el_label) { equal = el_value; }
            }.bind(this));
            if (els.length==0) this.close();
            ul.inject(drop);
            if (equal!=this.zerovalue) {
                //match!
                //this.close(); //hide on match
                this.setpair(null,equal);
            } 
        }.bind(this)}).get({'limit': this.limit, 'data': listfull ? '' : this.inputLabel.get('value')});
    },

    close: function() {
        this.dropdown.hide();
        $(document.body).removeEvents('click');
    },

    setpair: function(label,value) {
        if (label!=null) {
            this.inputLabel.set('value',label);
            this.inputLabel.fireEvent('change');
        }
        if (value!=null) this.inputValue.set('value',value);
        if (value!=this.zerovalue) this.inputLabel.setStyle('background-color','');
    }


});
